//
// vm-x86.c
// the x86 and x86_64 jit
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include "config.h"
#ifdef POTION_X86

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <math.h>
// #include <assert.h>
#include "always_assert.h"
#include "potion.h"
#include "internal.h"
#include "opcodes.h"
#include "asm.h"
#include "khash.h"
#include "table.h"

#define ARG_UNUSED PN_NIL // 内容不重要，作为占位符
#define ARG_VM    (sysargs)
#define ARG_CL    (sysargs + 1)
#define ARG_SELF  (sysargs + 2)
#define LOCAL(n)  (locals + n)
#define UPVAL(n)  (upvals + n)

// 结构类型 type 的柔性数组成员 field 的 n 号项。
#define FLEX_N(type, field, n) (offsetof(type, field) + (n) * sizeof(PN))

// 注意：不要传入复杂的表达式，因为会计算多次。
#define UNPACK4(n) _N(n, 0), _N(n, 8), _N(n, 16), _N(n, 24)
#define _N(n, bits) ((ptrdiff_t)n >> bits) & 0xFF

// TODO hsq 用 1 代替 !defined(NDEBUG) || defined(DEBUG) 则 test/data/html.pn 和 test/data/string.pn 失败。
#if !defined(NDEBUG) || defined(DEBUG)
  #define ASM_BYTES(...) ({ \
    uint16_t vec2[] = {__VA_ARGS__}; \
    for (size_t i = 0; i < sizeof(vec2) / sizeof(uint16_t); i++) { \
      assert(vec2[i] <= UINT8_MAX); \
    } \
    u8 vec[] = {__VA_ARGS__}; \
    /* TODO hsq 可以不用每次都更新 *asmp ？ */ \
    *asmp = potion_asm_write(P, *asmp, (char *)vec, sizeof(vec)); \
  })
#else
  #define ASM_BYTES(...) ({ \
    u8 vec[] = {__VA_ARGS__}; \
    *asmp = potion_asm_write(P, *asmp, (char *)vec, sizeof(vec)); \
  })
#endif

// 对64位，为第 x 号 PN(8字节) 在栈中地址： (%rbp -x*8 -1*8)
//  即 0 号地址为 (%rbp -8),
#define RBP(x)  (0x100 - ((x + 1) * sizeof(PN)))
// 未使用 #define RBPI(x) (0x100 - ((x + 1) * sizeof(int)))

#if __WORDSIZE == 64
  // TODO hsq 用 dynasm 。
  // TODO hsq 搜索 0x
  // REX prefix 0100 1000 表示使用 64 位操作数。
  #define X64_PRE 0x48
  // 未使用 #define X64_POST 0x48, 0x98
  // TODO hsq 可能还是原来使用 ASMN 更好；不过不能直接用，因为 32 位不可用。
  #define UNPACK8(n) UNPACK4(n), UNPACK4((((PN)n)/4294967296))
#endif

#include "vm-x86-asm.c"

// 调用函数，可传入任意数量参数。
//    注意，因为参数是同时传入，每个参数必需用不同的寄存器（即栈位）。
// TODO hsq 这些调用处大部分是 NYI 。
#define PROC_ARGS(...) { \
  int _n = FL_ARG_COUNT(__VA_ARGS__); \
  if (_n > 0) { \
    int _i = 0; \
    PN _args[] = {__VA_ARGS__}; \
    for (_i = 0; _i < _n; _i++) \
      X86_ARGO(_args[_i], _i); \
  } \
}

// 调用函数 f ，传入参数 ... ，结果在 %rax 。
#define CALL(f, ...) { \
  PROC_ARGS(__VA_ARGS__) \
  ASM_BYTES(set_rax(f), call_rax); \
}

// 调用函数 f ，传入参数 ... ，结果在 %rax 并存入寄存器 op.a 。
#define CALL2REG(f, ...) { \
  PROC_ARGS(__VA_ARGS__) \
  ASM_BYTES(set_rax(f), call_rax, rax2reg(op.a)); \
}

#define FILL_OFFSET (*asmp)->ptr[asmpos - 1] = ((*asmp)->len - asmpos);
// 操作数都是整数则执行算术运算，否则向首参发送相关消息。
#define X86_MATH(two, func, ops...) ({ \
  int asmpos = 0; \
  ASM_BYTES(reg2rax(op.a)); \
  if (two) { ASM_BYTES(reg2rdx(op.b)); } \
  ASM_BYTES(test_al(0x01), je1(0)); \
  asmpos = (*asmp)->len; \
  if (two) \
    ASM_BYTES(test_dl(0x01), je1(0)); \
  ASM_BYTES(ops, /* add, sub, ... */ jmp1(0)); \
  FILL_OFFSET; \
  if (two) { asmpos += 5; FILL_OFFSET; } \
  asmpos = (*asmp)->len; \
  /* args: 0-[a] */ \
  CALL(func, ARG_VM, op.a, op.b); \
  FILL_OFFSET; \
  ASM_BYTES(rax2reg(op.a)); \
})

// 比较两个操作数， reg[op.a] = !ops 。
#define X86_CMP(ops) ASM_BYTES( \
  reg2rdx(op.a), \
  reg2rax(op.b), \
  cmp_eax2edx, \
  ops, 10, /*  jle +10 */ \
  dword2reg(op.a, PN_TRUE), \
  jmp1(8), \
  dword2reg(op.a, PN_FALSE));

// 主调函数把 regn[0,~] 号位置变量写入 argn[0,~] 号参数
#define X86_ARGO(regn, argn) potion_x86_c_arg(P, asmp, 1, regn, argn)
// 被调函数接收 argn[0,~] 号参数写入 regn[0,~] 号变量位置
#define X86_ARGI(regn, argn) potion_x86_c_arg(P, asmp, 0, regn, argn)

// 处理跳转类指令，生成机器码 jmp ：如果 jpos 处的目标字节码在后面，则
//  在 jmps 加一个 PNJumps ，记录该机器码和目标字节码位置，以后再填入目标机器码位置；
//  如果目标在前面，则从 offs 取其机器码地址，减去当前地址生成相对偏移；
//  否则， jmp 指向下一条机器码。
// TODO hsq jpos + 1 ？ (asmp[0]->len) + 4 ？以及 jmpedit 调用处。
#define TAG_JMP(jpos) \
  if ((int)jpos >= (int)pos) { \
    jmps[*jmpc].from = asmp[0]->len + 1; \
    JMP4(0); \
    jmps[*jmpc].to = jpos + 1; \
    *jmpc = *jmpc + 1; \
  } else /* if ((int)jpos < (int)pos) */ { \
    size_t to = offs[jpos + 1] - ((asmp[0]->len + 1) + 4); \
    JMP4(to); \
  /* } else { */ \
    /* JMP4(0); */ \
  }

// 未使用 X86_DEBUG
#if 0
  // 单行注释: Linux & GCC: warning: multi-line comment [-Wcomment]
  #define X86_DEBUG() \
    ASM_BYTES(set_rax(potion_x86_debug), call_rax);

  // TODO: finish jit backtraces using this
  void potion_x86_debug() {
    Potion *P;
    int n = 0;
    _PN rax, *rbp, *sp;

    #if __WORDSIZE == 64
      __asm__ ("mov %%rax, %0;"
    #endif
              :"=r"(rax)
              );

    printf("RAX = %lx (%u)\n", rax, potion_type(rax));
    #if __WORDSIZE == 64
      __asm__ ("mov %%rbp, %0;"
    #endif
              :"=r"(sp)
              );

    P = (Potion *)sp[2];
    printf("Potion: %p (%p)\n", P, &P);

  again:
    n = 0;
    rbp = (unsigned long *)*sp;
    if (rbp > sp - 2 && sp[2] == (PN)P) {
      printf("RBP = %lx (%lx), SP = %lx\n", (PN)rbp, *rbp, (PN)sp);
      while (sp < rbp) {
        printf("STACK[%d] = %lx\n", n++, *sp);
        sp++;
      }
      goto again;
    }
  }
#endif

// 处理函数传参： argn[0,~] 号参数 <-> regn[0,~] 号位置变量；
//    64 位，前 6 参在寄存器，其余的被逆序压栈；因此，
//    被调函数接收时，从寄存器或栈中取出；主调函数传递时，写入寄存器或压栈。
//  out: 是否主调方。
// TODO hsq 细节见 bin.lua 。
// mimick c calling convention
static void potion_x86_c_arg(Potion *P, PNAsm * volatile *asmp, int out, int regn, int argn) {
#if __WORDSIZE == 64
  switch (argn) {
    case 0: ASM_BYTES(X64_PRE, out ? 0x8b : 0x89, 0x7d, RBP(regn)); break;
    case 1: ASM_BYTES(X64_PRE, out ? 0x8b : 0x89, 0x75, RBP(regn)); break;
    case 2: ASM_BYTES(X64_PRE, out ? 0x8b : 0x89, 0x55, RBP(regn)); break;
    case 3: ASM_BYTES(X64_PRE, out ? 0x8b : 0x89, 0x4d, RBP(regn)); break;
    case 4: ASM_BYTES(0x4c, out ? 0x8b : 0x89, 0x45, RBP(regn)); break;
    case 5: ASM_BYTES(0x4c, out ? 0x8b : 0x89, 0x4d, RBP(regn)); break;
    default:
      if (out) {
        ASM_BYTES(X64_PRE, 0x8B, 0x5d, RBP(regn)); // mov %rbp(A) %rbx
        if (argn == 6) {
          ASM_BYTES(X64_PRE, 0x89, 0x1c, 0x24); // mov %rbx (%rsp)
        } else {
          ASM_BYTES(X64_PRE, 0x89, 0x5c, 0x24, (argn - 6) * sizeof(PN)); // mov %rbx N(%rsp)
        }
      } else {
        ASM_BYTES(X64_PRE, 0x8b, 0x5d, (argn - 4) * sizeof(PN),
                  X64_PRE, 0x89, 0x5d, RBP(regn)); // mov %rbp(A) %rbx
      }
    break;
  }
#endif
}

/* maintain 16-byte stack alignment.  OS X in particular requires it, because
  * it expects to be able to use movdqa on things on the stack.
  * we factor in the offset from our saved ebp and return address, so that
  * adds 8 for x86 and 0 (mod 16) for x86_64.  */
#define ALIGN_RSP(need_bytes) (((need_bytes)+15)&~(15))

// 被调函数的 preluge 处理：压栈 %rbp ， %rbp=%rsp 。
void potion_target_setup(TARGET_PROTO_SIG, long need) {
  // ASM_BYTES(push_rbp, rsp2rbp);
  // ASM_BYTES(enter2(0));
  long rsp = ALIGN_RSP(need);
  ASM_BYTES(enter2(rsp));
}

// 移动栈指针 %rsp ，分配栈空间；对齐到 16 边界； need: 字节数。
void potion_target_stack(TARGET_PROTO_SIG, long need) {
  long rsp = ALIGN_RSP(need);
  if (rsp >= 0x80) {
    ASM_BYTES(alloc_locals4(rsp));
  } else {
    ASM_BYTES(alloc_locals1(rsp));
  }
}

// 接收前 3 个系统内部参数，并将 self 即第 3 个置于 0 号变量。
// 如果原型 f 有 upvalue ，则 .stack 后面的 LOCALSIZE 个变量置为 nil 。
// TODO hsq ARG_SELF 是否会太大？ X86_ARGI 首参的安全范围是 0～15 ？
void potion_target_registers(TARGET_PROTO_SIG, long sysargs) {
  // TODO hsq 如果没有 upvalue ，则局部变量不会初始化为 nil ？似乎不成问题，因为当前都有初值。
  // (Potion *, self) in the first argument slot, self in the first register
  X86_ARGI(ARG_VM, 0);
  X86_ARGI(ARG_CL, 1);
  X86_ARGI(ARG_SELF, 2);
  X86_ARGI(0, 2);
  // empty locals, since use of setlocal requires something there
  if (HAS_UPVALS(f)) {
    int locals = f->stacksize;
    for (PN_SIZE argx = 0; argx < LOCALSIZE(f); argx++) {
      DWORD2REG(LOCAL(argx), PN_NIL);
    }
  }
}

// 接收 3+arg 号参数写入 reg[0,~] 号变量（调用处给出的 reg 从 .stack 后开始）。
// TODO hsq 此处用户参数入栈，未做优化。
// TODO hsq 加入 inline 编译失败。
void potion_target_local(TARGET_PROTO_SIG, long reg, long arg) {
  X86_ARGI(reg, 3 + arg);
}

// 将闭包 f 的 upval 拷贝入栈：加载闭包 (ARG_CL) 到 %rax ，
//    加载 n 号 upval （通过在闭包 %rax 中的偏移）到 %rax ，
//    写入栈中对应位置。
// TODO hsq 第二步不要覆盖 %rax ，避免每个 upval 都加载闭包？
void potion_target_upvals(TARGET_PROTO_SIG, long upvals, long cl_pos, int upc) {
  int upi;
  for (upi = 0; upi < upc; upi++) {
    ASM_BYTES(
      reg2rax(cl_pos),
      unbox_rax(PNClosure, data) + (upi + 1) * sizeof(PN),
      // TODO hsq 这个指令的参数是单字节？若是，则可能溢出。
        // FLEX_N(struct PNClosure, data, upi + 1)); // 0x30(%rax)
        // sizeof(struct PNClosure) + (upi + 1) * sizeof(PN)
      rax2reg(UPVAL(upi)));
  }
}

// 在位置 asmj 中记录 偏移 dist 。
//  asmj: 跳转指令中记录偏移的位置； dist: 偏移 。
void potion_target_jmpedit(TARGET_PROTO_SIG, unsigned char *asmj, int dist) {
  *((int *)asmj) = dist;
}

// OP_MOVE: reg[op.b] -> %rax -> reg[op.a]
void potion_target_op_move(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  ASM_BYTES(reg2rax(op.b), rax2reg(op.a));
}

// OP_LOADPN: op.b -> reg[op.a]
void potion_target_op_loadpn(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  DWORD2REG(op.a, op.b);
}

PN potion_f_values(Potion *P, PN cl) {
  // TODO hsq 去掉 potion_fwd 出现几十个测试失败；可以预先调用一遍以避免在这里出现？
  // TODO hsq   然后可以消除函数调用，直接在汇编里访问 values ？以及其他几处函数调用？
  return potion_fwd(PN_PROTO(PN_CLOSURE(cl)->data[0])->values);
}

// OP_LOADVAL: f.values[op.b] -> reg[op.a]
void potion_target_op_loadval(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL(potion_f_values, ARG_VM, ARG_CL);
  ASM_BYTES(
    add_rax_(FLEX_N(struct PNTuple, set, op.b)),
    // UNPACK4(sizeof(struct PNTuple) + op.b * sizeof(PN))
    unbox_rax0,
    rax2reg(op.a));
}

// OP_SELF: self -> reg[op.a]
void potion_target_op_self(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  // TODO: optimize so that if this is followed by a BIND or MESSAGE, it'll just
  // use the self register directly.
  ASM_BYTES(reg2rax(ARG_SELF), rax2reg(op.a));
}

// #define REF_DATA sizeof(struct PNObject)
#define REF_DATA offsetof(struct PNWeakRef, data)

// %rax 是 PN_TWEAK 则跳过后面的 bytes 字节
#define X86_IS_REF_AND_JMP(byte_num) ASM_BYTES( \
  test_al(1), \
  jne1(16 + byte_num), \
  test_eax_(PN_REF_MASK), \
  je1(8 + byte_num), \
  cmpq_mem_in_eax(PN_TWEAK), \
  jne1(byte_num) \
  )

// OP_GETLOCAL: deref(local[op.b]) -> reg[op.a]
void potion_target_op_getlocal(TARGET_OP_SIG, long locals) {
  PN_OP op = PN_OP_AT(f, pos);
  ASM_BYTES(reg2rax(LOCAL(op.b)));
  if (HAS_UPVALS(f)) {
    X86_IS_REF_AND_JMP(4);
    ASM_BYTES(unbox_rax(PNWeakRef, data));
  }
  ASM_BYTES(rax2reg(op.a));
}

// OP_SETLOCAL:
void potion_target_op_setlocal(TARGET_OP_SIG, long locals) {
  PN_OP op = PN_OP_AT(f, pos);
  // TODO hsq 可以预先知道这个变量是否弱引用而不加判断直接解除引用？
  ASM_BYTES(reg2rdx(op.a));
  if (HAS_UPVALS(f)) {
    ASM_BYTES(reg2rax(LOCAL(op.b)));
    X86_IS_REF_AND_JMP(4);
    ASM_BYTES(box_rdx1(REF_DATA));  // mov %rdx %rax.data
    // TODO hsq PN_TOUCH(locals[op.b]);
    // ASM_BYTES(0xE9, UNPACK4(4));
  }
  // TODO hsq 如果 op.b 是弱引用，这条指令也生效什么作用？下面 setupval 没有这条指令。
  ASM_BYTES(rdx2reg(LOCAL(op.b)));
}

// OP_GETUPVAL: deref(upval[op.b]) -> reg[op.a]
void potion_target_op_getupval(TARGET_OP_SIG, long upvals) {
  PN_OP op = PN_OP_AT(f, pos);
  ASM_BYTES(
    reg2rax(UPVAL(op.b)),
    unbox_rax(PNWeakRef, data),
    rax2reg(op.a));
}

// OP_SETUPVAL: reg[op.a] -> deref(upval[op.b])
// TODO: place the upval in the write barrier (or have stack scanning handle weak refs)
void potion_target_op_setupval(TARGET_OP_SIG, long upvals) {
  PN_OP op = PN_OP_AT(f, pos);
  ASM_BYTES(
    reg2rdx(op.a),
    reg2rax(UPVAL(op.b)),
    box_rdx1(REF_DATA)); // mov %rdx %rax.data
  // TODO hsq PN_TOUCH(upvals[op.b]);
}

// OP_NEWTUPLE: () -> reg[op.a]
void potion_target_op_newtuple(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  // TODO hsq 尽量消除多层嵌套调用，特别是对于简单的函数；以及其他函数。
  CALL2REG(potion_tuple_empty, ARG_VM);
}

// OP_SETTUPLE: push op.b -> op.a[]
void potion_target_op_settuple(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL2REG(potion_tuple_push, ARG_VM, op.a, op.b);
}

// OP_SETTABLE：op.b -> op.a[op.a+1]
void potion_target_op_settable(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL2REG(potion_table_set, ARG_VM, op.a, op.a + 1, op.b);
}

// OP_NEWLICK:
void potion_target_op_newlick(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  // TODO hsq 如何可以不用入栈，而是直接把数值作为参数传入？
  int b = op.b;
  while (b < op.a + 2) {
    DWORD2REG(++b, PN_NIL);
  }
  CALL2REG(potion_lick, ARG_VM, op.a, op.a + 1, b);
}

// OP_GETPATH:
void potion_target_op_getpath(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL2REG(potion_obj_get, ARG_VM, op.a, op.b);
}

// OP_SETPATH:
void potion_target_op_setpath(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL(potion_obj_set, ARG_VM, op.a, op.a + 1, op.b);
}

// OP_ADD: 整数+ ｜ 消息+
void potion_target_op_add(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  // TODO hsq 解除封装，直接调用发消息函数？以及其他函数
  X86_MATH(1, potion_obj_add,
    // TODO hsq 溢出
    X64_PRE, OP_box_num, 0x44, 0x10, 0xFF // lea -1(%rax,%rdx,1),%rax
  );
}

// OP_SUB: 整数- ｜ 消息-
void potion_target_op_sub(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_sub,
    X64_PRE, 0x29, 0xD0, // sub %rdx %rax
    X64_PRE, 0xFF, 0xC0  // inc %rax
  );
}

// OP_MULT: 整数* ｜ 消息*
void potion_target_op_mult(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_mult,
    X64_PRE, unbox_num_in_edx,
    X64_PRE, 0xFF, 0xC8,        // dec %rax
    X64_PRE, 0x0F, 0xAF, 0xC2,  // imul %rdx %rax
    X64_PRE, 0xFF, 0xC0         // inc %rax
  );
}

#define X86_DIV(ops) \
    unbox_num_in_eax, \
    unbox_num_in_edx, \
    0x89, 0xD1,           /* mov %edx %ecx */ \
    0x89, 0xC2,           /* mov %eax %edx */ \
    0xC1, 0xFA, 0x1F,     /* sar 0x1f %edx */ \
    0xF7, 0xF9,           /* idiv %ecx */ \
    ops

// OP_DIV: 整数/ ｜ 消息/
void potion_target_op_div(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_div, X86_DIV(box_num_in_eax));
}

// OP_REM: 整数% ｜ 消息%
void potion_target_op_rem(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_rem, X86_DIV(box_num_in_edx_to_eax));
}

// OP_POW:
void potion_target_op_pow(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL2REG(potion_pow, ARG_VM, ARG_UNUSED, op.a, op.b);
}

// OP_NEQ: != ；指针比较的是指针本身的大小。
void potion_target_op_neq(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_CMP(OP_je1);
}

// OP_EQ: ==
void potion_target_op_eq(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_CMP(OP_jne1);
}

// OP_LT: <
void potion_target_op_lt(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_CMP(OP_jge1);
}

// OP_LTE: <=
void potion_target_op_lte(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_CMP(OP_jg1);
}

// OP_GT: >
void potion_target_op_gt(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_CMP(OP_jle1);
}

// OP_GTE: >=
void potion_target_op_gte(TARGET_OP_SIG) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_CMP(OP_jl1);
}

// OP_BITN: 整数～ ｜ 消息～
void potion_target_op_bitn(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(0, potion_obj_bitn,
    X64_PRE, 0xF7, 0xD0,  // not %rax
    X64_PRE, 0xFF, 0xC0   // inc %rax
  );
}

// OP_BITL: 整数<< ｜ 消息<<
void potion_target_op_bitl(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_bitl,
    unbox_num_in_eax,
    unbox_num_in_edx,
    0x89, 0xD1,     // mov %edx %ecx
    0xD3, 0xE0,     // shl %cl %eax
    box_num_in_eax
  );
}

// OP_BITR: 整数>> ｜ 消息>>
void potion_target_op_bitr(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_bitr,
    unbox_num_in_eax,
    unbox_num_in_edx,
    0x89, 0xD1,      // mov %edx %ecx
    0xD3, 0xF8,      // sar %cl %eax
    box_num_in_eax
  );
}

// OP_PIPE: 整数| ｜ 消息|
void potion_target_op_pipe(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_pipe,
    unbox_num_in_eax,
    unbox_num_in_edx,
    0x09, 0xD0,      // or %edx, %eax
    box_num_in_eax
  );
}

// OP_CARET: 整数^ ｜ 消息^
void potion_target_op_caret(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_caret,
    unbox_num_in_eax,
    unbox_num_in_edx,
    0x31, 0xD0,      // xor %edx, %eax
    box_num_in_eax
  );
}

// OP_AMP: 整数& ｜ 消息&
void potion_target_op_amp(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  X86_MATH(1, potion_obj_amp,
    unbox_num_in_eax,
    unbox_num_in_edx,
    0x21, 0xD0,      // and %edx, %eax
    box_num_in_eax
  );
}

// OP_DEF:
void potion_target_op_def(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL2REG(potion_def_method, ARG_VM, ARG_UNUSED, op.a, op.a + 1, op.b);
}

// OP_BIND:
void potion_target_op_bind(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  // args: 0-(0, 3), 1-(7, 3), 2-(7, 3)
  CALL2REG(potion_bind, ARG_VM, op.b, op.a)
}

// OP_MESSAGE:
void potion_target_op_message(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  // args: 0-(0, 3), 1-(7, 3), 2-(7, 3)
  CALL2REG(potion_message, ARG_VM, op.b, op.a);
}

// OP_JMP:
void potion_target_op_jmp(TARGET_OP_SIG, PNJumps *jmps, PN_SIZE *offs, PN_SIZE *jmpc) {
  PN_OP op = PN_OP_AT(f, pos);
  TAG_JMP(pos + op.b);
}

static void potion_x86_test_asm(TARGET_OP_SIG, int not) {
  PN_OP op = PN_OP_AT(f, pos);
  // TODO hsq 和 false 及 nil 的比较有几处，封装？
  ASM_BYTES(
    reg2rax(op.a),
    rax_is_false,
    je1(15),
    test_rax,
    je1(10),
    dword2reg(op.a, not ? PN_FALSE : PN_TRUE), // -A(%rbp) = TRUE
    jmp1(8),
    dword2reg(op.a, not ? PN_TRUE : PN_FALSE)); // -A(%rbp) = FALSE
}

// OP_TEST: 是否为真
void potion_target_op_test(TARGET_OP_SIG) {
  potion_x86_test_asm(P, f, asmp, pos, 0);
}

// OP_NOT: 是否为假
void potion_target_op_not(TARGET_OP_SIG) {
  potion_x86_test_asm(P, f, asmp, pos, 1);
}

// OP_CMP: <=>
void potion_target_op_cmp(TARGET_OP_SIG) {
  // potion_x86_test_asm(P, f, asmp, pos, 0);
  PN_OP op = PN_OP_AT(f, pos);
  // TODO hsq 直接利用 cpu 寄存器中的参数，而不是拷贝到栈中？留出 rax rdx
  // 来自 gcc -O1 -c -o cmp.o cmp.c && objdump -d cmp.o
  //  long cmp_c(long a, long b) {return a == b ? 0 : (a > b  ? 1 : -1);}
  ASM_BYTES(
    reg2rax(op.a),
    reg2rdx(op.b),
    X64_PRE, 0x31, 0xC9,              // xorq    %rcx,   %rcx
    0x4D, 0x31, 0xD2,                 // xorq    %r10,   %r10
    0x39, 0xD0,                       // cmpq    %rdx,   %rax
    0x0F, 0x9F, 0xC1,                 // setg    %cl
    X64_PRE, OP_box_num, 0x4C, 0x09, 0xFF,  // leaq    -1(%rcx,%rcx),  %rcx
    0x4C, 0x0F, 0x45, 0xD1,           // cmovneq %rcx,   %r10
    0x4C, 0x89, 0xD0,                 // movq    %r10,   %rax
    // TODO hsq 整数类型装箱，封装？
    box_num_in_rax,
    rax2reg(op.a));
}

// OP_TESTJMP: op.a 为真则跳转 op.b 字节
void potion_target_op_testjmp(TARGET_OP_SIG, PNJumps *jmps, PN_SIZE *offs, PN_SIZE *jmpc) {
  PN_OP op = PN_OP_AT(f, pos);
  ASM_BYTES(
    reg2rax(op.a),
    rax_is_false,
    je1(10),
    test_rax,
    je1(5));
  TAG_JMP(pos + op.b);
}

// OP_NOTJMP: op.a 为假则跳转 op.b 字节
void potion_target_op_notjmp(TARGET_OP_SIG, PNJumps *jmps, PN_SIZE *offs, PN_SIZE *jmpc) {
  PN_OP op = PN_OP_AT(f, pos);
  ASM_BYTES(
    reg2rax(op.a),
    rax_is_false,
    je1(5),
    test_rax,
    jne1(5));
  TAG_JMP(pos + op.b);
}

// OP_NAMED:
void potion_target_op_named(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL(potion_sig_find, ARG_VM, op.a, op.b - 1);
  ASM_BYTES(
    test_eax,
    0x78, 12, // js +12
    X64_PRE, 0xF7, 0xD8, // neg %rax
    reg2rdx(op.b),
  #if __WORDSIZE == 64
    X64_PRE, 0x89, 0x54, 0xC5, RBP(op.a + 2)); // mov %rdx, -A(%rbp,%rax,8)
  #endif
}

// OP_CALL: 如 op.a, ...
//  1. 是 VT, args... ，则用 VT.type 构建 PNObject 作为 self (置于 op.a+1) ，
//    如 VT.ctor 为 CL ，则进入 2. ，否则进入 3. 。
//  2. 是 CL, self, args... ，则调用 CL.method(VM, CL, self, args...) ；
//  3. obj(!CL !VT), args... ，则 obj 作为 self (置于 op.a+1) ，
//    调用 obj.call.method(VM, obj.call, obj, ...) ；
// TODO hsq 字节码实现是否跟这个逻辑一样？
// TODO: check for bytecode nodes and jit them as well?
void potion_target_op_call(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  int argc = op.b - op.a;

  // check type of the closure
  ASM_BYTES(
    reg2rax(op.a),
    test_al(PN_FNUMBER),
    jne1(64), // 数字 --> L_NOT_VT_CL
    test_eax_(PN_REF_MASK),
    je1(56)); // 其他 primitive:  nil bool --> L_NOT_VT_CL
    // 这条指令没必要，去掉也能通过测试。
    // X64_PRE, 0x83, 0xE0, PN_REF_MASK // and ~PRIMITIVE %rax

  // if a class, pull out the constructor
  ASM_BYTES(cmpq_mem_in_eax(PN_TVTABLE), jne1(40)); // --> L_check_closure
  CALL(potion_object_new, ARG_VM, ARG_UNUSED, op.a);
  ASM_BYTES(
    rax2reg(op.a + 1), // obj
    reg2rax(op.a),
    unbox_rax(PNVtable, ctor),
    rax2reg(op.a),

    // check type of the closure
    // L_check_closure:
    cmpq_mem_in_eax(PN_TCLOSURE),
    je1(28), // --> L_get_closure_method

    // if not a closure, get the type's closure
    // L_NOT_VT_CL:
    rax2reg(op.a + 1)); // 第三参数 self
  CALL(potion_obj_get_call, ARG_VM, op.a); // [b] callq *%rax
  ASM_BYTES(
    rax2reg(op.a), // 第二参数 closure

    // get the closure's function
    // L_get_closure_method:
    unbox_rax(PNClosure, method));

  // (Potion *, CL) as the first argument
  X86_ARGO(ARG_VM, 0);
  X86_ARGO(op.a, 1);
  while (--argc >= 0) X86_ARGO(op.a + argc + 1, argc + 2);
  ASM_BYTES(call_rax, rax2reg(op.a));
}

// OP_CALLSET:
void potion_target_op_callset(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL2REG(potion_obj_get_callset, ARG_VM, op.b);
}

// OP_RETURN:
void potion_target_op_return(TARGET_OP_SIG) {
  ASM_BYTES(reg2rax(0), leave_ret);
}

// 取 cl 的 proto 的 i 号子 proto ，以其 .jit 为 method 为其创建闭包。
PN potion_f_protos(Potion *P, PN cl, PN i) {
  PN protos = PN_PROTO(PN_CLOSURE(cl)->data[0])->protos;
  struct PNProto * proto = PN_PROTO(PN_TUPLE_AT(protos, i));
  vPN(Closure) c = (struct PNClosure *)potion_closure_new(P, JIT_CODE(proto->jit),
    proto->sig, UPVALSIZE(proto) + 1);
  // c->method = JIT_CODE(proto->jit);
  c->data[0] = (PN)proto;
  return (PN)c;
}

// OP_PROTO:
void potion_target_op_proto(TARGET_PROTO_SIG, PN_SIZE *pos, long upvals, long sysargs, long locals) {
  PN_OP op = PN_OP_AT(f, *pos);
  PN proto = PN_TUPLE_AT(f->protos, op.b);
  DWORD2REG(op.a, op.b);
  CALL2REG(potion_f_protos, ARG_VM, ARG_CL, op.a);
  PN_TUPLE_COUNT(PN_PROTO(proto)->upvals, i, {
    (*pos)++;
    PN_OP opp = PN_OP_AT(f, *pos);
    if (opp.code == OP_GETUPVAL) {
      ASM_BYTES(reg2rdx(UPVAL(opp.b)));
    } else if (opp.code == OP_GETLOCAL) {
      CALL(potion_ref, ARG_VM, LOCAL(opp.b));
      ASM_BYTES(rax2rdx, rax2reg(LOCAL(opp.b)));
    } else {
      fprintf(stderr, "** missing an upval to proto %p\n", (void *)proto);
    }
    ASM_BYTES(
      reg2rax(opp.a), // mov cl %rax
      X64_PRE, 0x89, 0x50, // mov %rdx N(%rax)
        // sizeof(struct PNClosure) + (sizeof(PN) * (i + 1));
        FLEX_N(struct PNClosure, data, i + 1));
  });
}

// OP_CLASS:
void potion_target_op_class(TARGET_OP_SIG, long sysargs) {
  PN_OP op = PN_OP_AT(f, pos);
  CALL2REG(potion_vm_class, ARG_VM, op.b, op.a);
}

// 空。
void potion_target_finish(TARGET_PROTO_SIG) {}

// 生成：查找虚表方法，结果置于 %EAX 然后退出函数；不带函数初始化代码。
// TODO hsq 并不具有缓存功能。也没有性能优势。
void potion_target_mcache(Potion *P, vPN(Vtable) vt, PNAsm * volatile *asmp) {
  #if __WORDSIZE != 64
  #endif
  KH_EACH_DESC(PN, vt->methods, {
    ASM_BYTES(
      cmp_edi_(PN_UNIQ(kh_key(PN, t, k))), // cmp NAME %edi
      jne1(11),
      set_rax(kh_val(PN, t, k)), // mov CL %rax
      ret);
  });
  ASM_BYTES(set_eax(PN_NIL), ret);
}

// 生成：查找虚表属性，结果置于 %RAX 然后退出函数；不带函数初始化代码。
void potion_target_ivars(Potion *P, PN ivars, PNAsm * volatile *asmp) {
  #if __WORDSIZE != 64
  #endif
  PN_TUPLE_EACH(ivars, i, v, {
    ASM_BYTES(
      cmp_edi_(PN_UNIQ(v)), // cmp UNIQ %edi
      jne1(6),
      set_eax(i),
      ret);
  });
  ASM_BYTES(set_rax(-1), ret);
}

// 构造 PNTarget potion_target_x86
MAKE_TARGET(x86);

#endif